package core1;

import java.util.LinkedList;
import java.util.List;

import processing.core.PApplet;

public class ChordQuery implements Drawable {
	
	Globals globals = Globals.getInstance();
	
	private int latency = globals.queryLatency; // Number of frames each query takes (FPS is 60). Adjust to simulate network latency;
	private int time = 0; // Number of elapsed time units since query began
	private boolean done = false; // Tells if the query is completed or not. The object may be discarded if it is.
	
	private StandAloneChordNode startNode;
	private StandAloneChordNode endNode;
	private List<StandAloneChordNode> path;
	private PApplet parent;

	private long targetkey;

	private boolean drawKey =false;
	
	public boolean isDone() {
		return done;
	}

	@Override
	public void draw(float scale) {
		if(globals.drawAlgo){
		
		if (!globals.animateQueries)
		drawStatic(scale);
		else
		{
			if (globals.DrawStaticPathWhileAnimating)
				drawStatic(scale);
			
			animate(scale);
		}}

	}
	
	private void animate(float scale) {
		time += globals.querySpeed;  // increment the time
		
		int currentSection = time / latency ; // the section the query is currently in
		float pathCompleted = (time % latency)/(float) latency; // the fraction of a section that is currently traversed
		if (currentSection <  path.size()-1)
		{
			parent.pushStyle();
			parent.stroke(globals.queryColor);
			parent.strokeWeight(2/scale);	
			
				
			if (globals.HighlightCurrentPathSection)
				DrawOneCurve(scale, path.get(currentSection), path.get(currentSection+1));
			
			if (globals.animateOnCurve != Globals.NOTHING)
				DrawQueryMessage(scale, path.get(currentSection), path.get(currentSection+1), pathCompleted);
			
			parent.popStyle();
			
		}
		else
			done = true;
		
	}
	
	private void DrawQueryMessage(float scale,
			StandAloneChordNode from,
			StandAloneChordNode to, float pathCompleted) {
		
		float curvature = (1-globals.curvature);
		float thatx = to.getXpos();
		float thaty = to.getYpos();
		float x = from.getXpos();
		float y = from.getYpos();

		// find x and y coordinates on the curve
		float qx = parent.bezierPoint(x, x*curvature, thatx*curvature, thatx, pathCompleted);
		float qy = parent.bezierPoint(y, y*curvature, thaty*curvature, thaty, pathCompleted);
		
		parent.stroke(globals.queryColor);
		parent.fill(globals.queryColor);
		
		parent.strokeWeight(5/scale);	
	    
		if ( globals.animateOnCurve == Globals.BALL)
		parent.ellipse(qx, qy, globals.queryBallRadius/scale, globals.queryBallRadius/scale) ;
		
		if (globals.animateOnCurve == Globals.LINE){
		    float previousPathCompleted = (float) Math.max(0,pathCompleted-0.3);
			float pqx = parent.bezierPoint(x, x*curvature, thatx*curvature, thatx, previousPathCompleted);
			float pqy = parent.bezierPoint(y, y*curvature, thaty*curvature, thaty, previousPathCompleted);
			parent.line(pqx, pqy, qx, qy);
			
			}
		
	}

	// draw the path as a static image. No animation or time delay

	private void drawStatic(float scale)
	{
		
		int opacity = globals.queryOpacity;
		
		
		parent.pushStyle();
		parent.stroke(globals.setOpacity(globals.queryColor,opacity));
		parent.strokeWeight(2/scale);
		StandAloneChordNode prev = startNode;
	
		
		for (StandAloneChordNode n:path)
		{
			if (prev != n)
			{
			DrawOneCurve(scale, prev, n);
			
			}
			prev=n;
			
		}
		if (drawKey){
		float targetx = globals.getRingPositionX(targetkey);
		float targety = globals.getRingPositionY(targetkey);
		parent.stroke(globals.setOpacity(globals.queryColor,128+64));
		float size = 7/scale;
		parent.strokeCap(parent.ROUND);
		parent.strokeWeight(1/scale);
		parent.line(targetx-size, targety-size, targetx+size, targety+size);
		parent.line(targetx-size, targety+size, targetx+size, targety-size);
		//parent.ellipse(targetx, targety, size, size);
		
		
		}
		parent.popStyle();
	}
	
	// draw a simple bez curve between any two nodes
	private void DrawOneCurve(float scale, StandAloneChordNode from, StandAloneChordNode to){
		
		float curvature = (1-globals.curvature);
		
		float thatx = to.getXpos();
		float thaty = to.getYpos();
		float x = from.getXpos();
		float y = from.getYpos();
		parent.bezier(x,y,x*curvature,y*curvature,thatx*curvature,thaty*curvature,thatx,thaty);
	}
	
	
	public ChordQuery(PApplet parent, StandAloneChordNode startNode, StandAloneChordNode endNode) {
		super();
		this.parent = parent;
		this.startNode = startNode;
		this.endNode = endNode;
		build();
		
	}
	public ChordQuery(PApplet parent, StandAloneChordNode startNode, StandAloneChordNode endNode, long targetKey) {
		super();
		this.parent = parent;
		this.startNode = startNode;
		this.endNode = endNode;
		this.targetkey = targetKey;
		this.drawKey = true;
		
		build();
		
	}

   // Build the path. Do this only once per query as it is a little slow. 
	public void build(){
		path = new LinkedList<StandAloneChordNode>();
		
		//path.add(startNode);
		StandAloneChordNode next = startNode;
		StandAloneChordNode prev = null;
		
		do
		{
			if (next != prev)
			path.add(next);
			
			prev = next;
			
			if (globals.networkMode==globals.CHORD_MODE)
			next = (StandAloneChordNode) next.ClosestFinger(endNode.getKey());
			
			if (globals.networkMode==globals.SIMPLE_MODE)
				next = (StandAloneChordNode) next.getNext();
			if (globals.networkMode==globals.MESH_MODE)
				next = endNode;
			
		} while (next != endNode);
		
		path.add(endNode);
		
	}

}
